package com.getee.tianchaohongyu.kopier.parser

import com.getee.tianchaohongyu.kopier.NodeData


/**
 * 解析器
 * created zpf on 2019/2/18 0018
 */
abstract class AbstractParser {

    /**这些范围的字符码则忽略*/
    protected open val ignoreCharRange: CharRange = (0.toChar())..(32.toChar())

    /**这些字符码则忽略*/
    protected open var ignoreChars: MutableList<Char> = ArrayList()

    /**这些字符码将当作错误处理*/
    protected open var errorChars: MutableList<Char> = ArrayList()

    /**换行符字符码*/
    protected val BR: Char = '\n'

    /**
     * 解析字符码迭代器
     * @param nodeData 节点数据
     * @param iterator 迭代器
     */
    abstract fun parse(nodeData: NodeData, iterator: Iterator<Int>): NodeData

    /**
     * 在字符码迭代器中寻找指定的字符
     * @param iterator 字符码迭代器
     * @param targetChars 找到这些字符之一为止
     */
    protected open fun find(iterator: Iterator<Int>, vararg targetChars: Char): FindResult {
        val builder = StringBuilder()
        val position = StreamPosition()
        while (iterator.hasNext()) {
            val chr = iterator.next().toChar()
            position.cols++
            when {
                chr in targetChars -> return FindResult(true, position, chr, builder.toString())
                chr in errorChars -> throw CharException(
                    targetChars.toList(),
                    chr,
                    FindResult(false, position, chr, builder.toString())
                )
                chr == BR -> position.nextRow()
                chr !in ignoreCharRange && chr !in ignoreChars -> builder.append(chr)
            }
        }
        return FindResult(false, position, 0.toChar(), builder.toString())
    }
}

/**
 * 流移动位置
 * @param rows 移动行数
 * @param cols 移动列数
 */
data class StreamPosition(var rows: Int = 0, var cols: Int = 0) {

    /**
     * 切换到下一行
     */
    fun nextRow() {
        rows++
        cols = 0
    }
}

/**
 * 寻找结果
 * @param have 是否找到目标字符
 * @param position 移动距离
 * @param char     找到的字符
 * @param contents 内容
 */
data class FindResult(var have: Boolean, var position: StreamPosition, var char: Char, var contents: String)

/**
 * 寻找异常
 * @param expectations 期待字符列表
 * @param real 出现的实际字符
 * @param result 寻找结果
 */
data class CharException(val expectations: List<Char>, val real: Char, val result: FindResult) :
    RuntimeException("字符'${real}'不合法，应该为这些字符:${expectations}。")